#include "node.h"
#include "director.h"
#include "../action/action.h"
#include "../../base/bits.h"

OPEN_JLIB_NS


void Node::addChild (Node *child, s32 stepOrder, s32 drawOrder) {
  debug_assert(child);
  debug_assert(!child->_parent);
  debug_assert(child->_director == _director);
  child->retain();
  child->_parent = this;
  child->_stepOrder.major = stepOrder;
  child->_stepOrder.minor = __orderSeed++;
  child->_drawOrder.major = drawOrder;
  child->_drawOrder.minor = __orderSeed++;
  _stepChildren.push_back(&child->_stepSiblings);
  _drawChildren.push_back(&child->_drawSiblings);
  _builtinFlags |= StepQueueDirty | DrawQueueDirty;
}

void Node::removeSelf (bool cleanup) {
  debug_assert(_parent);
  if (cleanup)
    removeAllChildren();
  stopAllActions();
  // TODO: unregister all event listeners
  _stepSiblings.detach();
  _drawSiblings.detach();
  _parent = nullptr;
  release();
}

void Node::removeChild (Node *child, bool cleanup) {
  debug_assert(child);
  debug_assert(child->_parent == this);
  child->removeSelf(cleanup);
}

void Node::removeAllChildren () {
  for (auto child : stepQueue())
    child->removeSelf(true);
}

Node::~Node () {
  removeAllChildren();
}

void Node::setStepOrder (s32 order) noexcept {
  _stepOrder.major = order;
  if (_parent)
    _parent->_builtinFlags |= StepQueueDirty;
}

void Node::setDrawOrder (s32 order) noexcept {
  _drawOrder.major = order;
  if (_parent)
    _parent->_builtinFlags |= DrawQueueDirty;
}

s64 Node::compareOrder (List *lhs, List *rhs) noexcept {
  auto &l = lhs->cast(&Node::_stepSiblings)->_stepOrder;
  auto &r = rhs->cast(&Node::_stepSiblings)->_stepOrder;
  return l.order - r.order;
}

void Node::reorderStepQueue () noexcept {
  if (_builtinFlags & StepQueueDirty) {
    _stepChildren.fast_sort(&compareOrder);
    _builtinFlags &= ~StepQueueDirty;
  }
}

void Node::reorderDrawQueue () noexcept {
  if (_builtinFlags & DrawQueueDirty) {
    _drawChildren.fast_sort(&compareOrder);
    _builtinFlags &= ~DrawQueueDirty;
  }
}

void Node::setPosition (Vec3 position) {
  _position = position;
  _builtinFlags |= AllTransformDirtyMask;
}

void Node::setRotation (Vec3 rotation) {
  _rotation = rotation;
  _builtinFlags |= AllTransformDirtyMask;
}

void Node::setScale (Vec3 scale) {
  _scale = scale;
  _builtinFlags |= AllTransformDirtyMask;
}

const Mat4& Node::getLocalMatrix () {
  if (_builtinFlags & AllTransformDirtyMask) {
    auto p = getPosition();
    auto m = Mat4::translate(p);
    auto r = getRotation();
    auto s = getScale();
    m = m * Mat4::rotatex(r.x());
    m = m * Mat4::rotatey(r.y());
    m = m * Mat4::rotatez(r.z());
    m = m * Mat4::scale(s);
    _localTransform = m;
    _builtinFlags &= ~LocalTransformDirty;
  }
  return _localTransform;
}

const Mat4& Node::getWorldMatrix () {
  if (_builtinFlags & WorldTransformDirty) {
    auto m = getLocalMatrix();
    if (_parent)
      m = _parent->getWorldMatrix() * m;
    _worldTransform = m;
    _builtinFlags &= ~WorldTransformDirty;
  }
  return _worldTransform;
}

const Mat4& Node::getLocalInverseMatrix () {
  if (_builtinFlags & LocalInverseTransformDirty) {
    _localInverseTransform = -getLocalMatrix();
    _builtinFlags &= ~LocalInverseTransformDirty;
  }
  return _localInverseTransform;
}

const Mat4& Node::getWorldInverseMatrix () {
  if (_builtinFlags & WorldInverseTransformDirty) {
    _worldInverseTransform = -getWorldMatrix();
    _builtinFlags &= ~WorldInverseTransformDirty;
  }
  return _worldInverseTransform;
}

void Node::dispatchStep (f32 dt) {
  reorderStepQueue();
  auto queue = stepQueue();
  auto iter = queue.begin();
  auto end = queue.end();
  while (iter != end) {
    auto node = *iter;
    if (node->stepOrder() >= 0)
      break;
    ++iter;
    if (node->willRemove())
      node->removeSelf(true);
    else if (node->isStepEnabled())
      node->dispatchStep(dt);
  }
  step(dt);
  while (iter != end) {
    auto node = *iter++;
    if (node->willRemoved())
      node->removeSelf(true);
    else if (node->isStepEnabled())
      node->dispatchStep(dt);
  }
}

void Node::dispatchDraw (const Mat4 &pv, const Mat4 &p, const Mat4 &v, const Mat4 &m, Vec4 c) {
  reorderDrawQueue();
  auto queue = drawQueue();
  auto iter = queue.begin();
  auto end = queue.end();
  auto model = m * getLocalMatrix();
  auto color = getColor();
  if (_builtinFlags & CascadeColor)
    color *= c;
  for (; iter != end; ++iter) {
    if (iter->drawOrder() >= 0)
      break;
    if (iter->isDrawEnabled())
      iter->dispatchDraw(pv, p, v, model, color);
  }
  draw(pv, p, v, model, color);
  for (; iter != end; ++iter) {
    if (iter->isDrawEnabled())
      iter->dispatchDraw(pv, p, v, model, color);
  }
}

void Node::runAction (Action* action) {
  debug_assert(action);
  action->setTarget(this);
}

void Node::stopAction (Action* action) {
}

void Node::stopAllActions () {
}


void Node::setStepEnabled (bool enable) noexcept {
  if (enable)
    _builtinFlags |= StepEnabled;
  else
    _builtinFlags &= ~StepEnabled;
}
void Node::setDrawEnabled (bool enable) noexcept {
  if (enable)
    _builtinFlags |= DrawEnabled;
  else
    _builtinFlags &= ~DrawEnabled;
}
void Node::setTouchEnabled (bool enable) noexcept {
  if (enable)
    _builtinFlags |= TouchEnabled;
  else {
    if (_root)
      _root->getTouchManager()->releaseNode(this);
    _builtinFlags &= ~TouchEnabled;
  }
}
void Node::setMouseEnabled (bool enable) noexcept {
  if (enable)
    _builtinFlags |= MouseEnabled;
  else {
    if (_root)
      _root->getMouseManager()->releaseNode(this);
    _builtinFlags &= ~MouseEnabled;
  }
}
void Node::setKeyEnabled (bool enable) noexcept {
  if (enable)
    _builtinFlags |= KeyEnabled;
  else {
    if (_root)
      _root->getKeyManager()->releaseNode(this);
    _builtinFlags &= ~KeyEnabled;
  }
}


u32 Node::__orderSeed = 1;


CLOSE_JLIB_NS
